package au.com.iglootech.core;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 
 * @author zhuhome
 * 
 * @param <A>
 * @param <B>
 */
public class IGTTuple2<A, B> implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = -5292350432152233153L;
	private A a;
	private B b;

	/**
	 * 
	 */
	public IGTTuple2() {

	}

	/**
	 * 
	 * @param a
	 *            first element
	 * @param b
	 *            second element
	 */
	public IGTTuple2(final A a, final B b) {
		this.a = a;
		this.b = b;
	}

	/**
	 * 
	 * @return first element
	 */
	public A getFirst() {
		return a;
	}

	/**
	 * 
	 * @return second element
	 */
	public B getSecond() {
		return b;
	}

	/**
	 * @param <A>
	 *            type
	 * @param <B>
	 *            type
	 * @param a
	 *            first element
	 * @param b
	 *            second element
	 * 
	 * @return tuple2
	 */
	public static <A, B> IGTTuple2<A, B> make(final A a, final B b) {
		return new IGTTuple2<A, B>(a, b);
	}

	@Override
	public final boolean equals(final Object otherObj) {
		if (otherObj instanceof IGTTuple2) {
			@SuppressWarnings("rawtypes")
			IGTTuple2 other = (IGTTuple2) otherObj;
			return this.getFirst().equals(other.getFirst())
					&& this.getSecond().equals(other.getSecond());
		}
		return false;
	}

	@Override
	public final int hashCode() {
		return a.hashCode() * 7 + b.hashCode() * 31;
	}

	/**
	 * @param <A>
	 *            type
	 * @param <B>
	 *            type
	 * 
	 * @param tupleList
	 *            list
	 * @return tuple2
	 */
	public static <A, B> List<A> getFirsts(final List<IGTTuple2<A, B>> tupleList) {
		List<A> ret = new ArrayList<A>();

		if (tupleList == null) {
			return ret;
		}

		for (IGTTuple2<A, B> tuple : tupleList) {
			ret.add(tuple != null ? tuple.getFirst() : null);
		}
		return ret;
	}

	/**
	 * @param <A>
	 *            type
	 * @param <B>
	 *            type
	 * @param tupleList
	 *            list
	 * @return <A, B>
	 */
	public static <A, B> List<B> getSeconds(
			final List<IGTTuple2<A, B>> tupleList) {
		List<B> ret = new ArrayList<B>();

		if (tupleList == null) {
			return ret;
		}

		for (IGTTuple2<A, B> tuple : tupleList) {
			ret.add(tuple != null ? tuple.getSecond() : null);
		}
		return ret;
	}

	/**
	 * @param <A>
	 *            type
	 * @param <B>
	 *            type
	 * @param tupleList
	 *            list
	 * @return tuple2
	 */
	public static <A, B> Map<A, B> toMap(final List<IGTTuple2<A, B>> tupleList) {
		Map<A, B> ret = new HashMap<A, B>();

		if (tupleList == null) {
			return ret;
		}

		for (IGTTuple2<A, B> tuple : tupleList) {
			ret.put(tuple != null ? tuple.getFirst() : null,
					tuple != null ? tuple.getSecond() : null);
		}
		return ret;
	}

	/**
	 * @param <A>
	 *            type
	 * @param <B>
	 *            type
	 * @param values
	 *            map
	 * @return <A, B> List<IGTTuple2<A, B>>
	 */
	public static <A, B> List<IGTTuple2<A, B>> fromMap(final Map<A, B> values) {
		List<IGTTuple2<A, B>> converted = new ArrayList<IGTTuple2<A, B>>();

		for (Map.Entry<A, B> entry : values.entrySet()) {
			converted
					.add(new IGTTuple2<A, B>(entry.getKey(), entry.getValue()));
		}

		return converted;
	}

	/**
	 * @param <A>
	 *            type
	 * @param <B>
	 *            type
	 * @param tupleList
	 *            list
	 * @param tuple
	 *            tuple
	 * @return tuple
	 */
	public static <A, B> boolean contains(
			final List<IGTTuple2<A, B>> tupleList, final IGTTuple2<A, B> tuple) {
		if (tupleList == null) {
			return false;
		}
		for (IGTTuple2<A, B> t : tupleList) {
			if (t != null && t.equals(tuple)) {
				return true;
			}
		}

		return false;
	}

	/**
	 * @param a
	 *            first element
	 */
	public void setFirst(final A a) {
		this.a = a;
	}

	/**
	 * @param b
	 *            second element
	 */
	public void setSecond(final B b) {
		this.b = b;
	}
}